
import java.awt.Color;
import java.util.ArrayList;

import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.joints.Joint;
import org.jbox2d.dynamics.joints.JointEdge;
import org.jbox2d.dynamics.joints.RevoluteJoint;

import pulpcore.Input;
import pulpfizz.physics.Actor;
import pulpfizz.physics.BodyUtils;
import pulpfizz.physics.JointBreakable;
import pulpfizz.physics.JointBreaker;
import pulpfizz.physics.JointUtils;
import pulpfizz.physics.Material;
import pulpfizz.physics.NewWorld;
import pulpfizz.physics.PhysicsUtils;
import pulpfizz.pulp.body.BodySprite;
import pulpfizz.pulp.body.Java2DBodySprite;
import pulpfizz.pulp.body.PhysicsLayer;

public class MadSnake extends Actor {
	public static int MAD_SNAKE_GROUP = 2;
	PhysicsLayer physics;
	NewWorld world;
	ArrayList<BodySprite> sprites = new ArrayList<BodySprite>();
	
	ArrayList<JointBreaker> jointBreakers = new ArrayList<JointBreaker>();
	
	float totalMass;
	
	Body head;
	
	int originalSegs;

	public MadSnake(Vec2 headAt, Vec2 tailAt, int segs, PhysicsLayer _phys) {
		super();
		originalSegs = segs;
		physics = _phys;
		this.world = physics.getWorld();
		if (segs <= 0) throw new IllegalArgumentException("Must have positive number of segments in a MadSnake.");
		this.addToGroup(MAD_SNAKE_GROUP);
		this.setName("Mad snake, id #"+this.getID());
		Vec2 diff = tailAt.sub(headAt);
		float angle = PhysicsUtils.angle(diff) - (float)Math.PI*.5f;
		float length = diff.length();
		diff.x /= segs;
		diff.y /= segs;
		float segHeight = 1.1f*length / segs;
		float maxSegWidth = segHeight;
		float minSegWidth = 0.04f;
		head = BodyUtils.createBox(world, headAt.x,headAt.y,maxSegWidth,segHeight, angle);
		Material m = Material.PLASTIC();
		m.setFriction(1.0f);
		m.applyAll(head);
		Java2DBodySprite spr = new Java2DBodySprite(head, physics,3,Color.BLUE,Color.CYAN);
        sprites.add(spr);
        physics.add(spr);
        this.addBody(head);
        Body last = head;
        Vec2 currVec = headAt.clone();
        float breakForce = 50.0f;
        for (int i=1; i<segs; ++i) {            
        	currVec.addLocal(diff);
        	float currWidth = ( maxSegWidth * (segs-i) + minSegWidth * (i) ) / segs;
        	Body box2 = BodyUtils.createBox(world, currVec.x,currVec.y,currWidth,segHeight, angle);
        	m.applyAll(box2);
        	Java2DBodySprite lastSpr = new Java2DBodySprite(box2, physics,3,Color.BLUE,Color.CYAN);
        	physics.add(lastSpr);
        	this.addBody(box2);
        	sprites.add(lastSpr);
        	RevoluteJoint rj = JointUtils.createRevoluteJoint(last, box2, currVec.x-diff.x*.5f, currVec.y-diff.y*.5f);//1.9f+.18f*i);
        	rj.enableLimit(true);
        	rj.setLimits(-0.5f, 0.5f);
        	JointBreaker jb = new JointBreaker(rj,breakForce);
        	breakForce *= .8f;
        	//world.registerPostStep(jb);
        	jointBreakers.add(jb);
        	last = box2;
            last.m_linearDamping = 0.2f*i;
        }
        
        totalMass = 0.0f;
        for (Body b:this.getBodies()) {
        	totalMass += b.getMass();
        }
        
        baseCountToSwitch = (int)(Math.random()*200 + 25);
        countToSwitchDirection = baseCountToSwitch;
	}
	
	private void recalculateMassFromHead() {
		System.out.println("Old mass: "+totalMass);
		float oldMass = totalMass;
		Body curr = head;
		totalMass = head.getMass();
		boolean done = false;
		Body skipBody = null;
		int nSegs = 1;
		while (!done) {
			boolean foundConnected = false;
			for (Body b:this.getBodies()) {
				if (b == skipBody) continue;
				if (curr.isConnected(b)) {
					totalMass += b.getMass();
					foundConnected = true;
					skipBody = curr;
					curr = b;
					++nSegs;
				}
			}
			if (!foundConnected) done = true;
		}
		torqueFactor *= totalMass / oldMass;
		System.out.println(nSegs+" segs, New mass: "+totalMass+", ratio "+(totalMass/oldMass));
	}
	
	@Override
	public boolean shouldCollide(Actor a) {
		//if (a.isInGroup(MAD_SNAKE_GROUP)) return false;
		return true;
	}
	
	private float direction = 1.0f;
	private int countToSwitchDirection;
	private int baseCountToSwitch;
	private float originalTorqueFactor = 10.0f;
	private float torqueFactor = originalTorqueFactor;
	
	@Override
	public void update(int elapsedTime) {
		if (--countToSwitchDirection < 0) {
			int randomized = (int)(Math.random()*50 - 25);
			countToSwitchDirection = baseCountToSwitch+randomized;
			if (countToSwitchDirection < 25) countToSwitchDirection = 25;
			direction *= -1.0f;
		}
		float torqueToApply = direction*totalMass*torqueFactor;
		head.applyTorque(torqueToApply);
		ArrayList<JointBreaker> toRemove = new ArrayList<JointBreaker>();
		for (JointBreaker jb:jointBreakers) {
			jb.step(elapsedTime, 0);
			if (jb.getJoint() == null) toRemove.add(jb);
		}
		for (JointBreaker jb:toRemove) {
			jointBreakers.remove(jb);
			recalculateMassFromHead();
		}
	}
}
